package com.oig.sys.workflow.util;


import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.oig.common.exception.BadRequestException;
import com.oig.common.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;

@Slf4j
public class MongoExpCompare {
    private MongoExpCompare(){}

    private static final String C_EQ = "$eq";    // =
    private static final String C_NE = "$ne";    // !=
    private static final String C_LIKE = "$like"; // like
//    private static final String C_NLIKE = "$nlike"; // not like 先不支持
    private static final String C_GT = "$gt";    // >
    private static final String C_GTE = "$gte";  // >=
    private static final String C_LT = "$lt";    // <
    private static final String C_LTE = "$lte";  // <=
    private static final String C_IN = "$in";    // in
    private static final String C_NIN = "$nin";  // not in
    private static final String C_BETWEEN = "$between"; // range
    private static final String C_NULL = "$null";
    private static final String C_NOTNULL = "$notnull";
    private static final String C_DY_NOW = "$dynow"; //

    private static final String C_AND = "$and";
    private static final String C_OR = "$or" ;

    /**
     * 匹配条件格式
     * {"$and": [
     *      {"optime":{"$between":[1614528000000,1616256000000]}},
     *      {"price": {$eq: 1.99 }},
     *      {"warehouseId":{"$in":[1,3,4]}},
     *      {"amt":{$ne: 1.99}},
     *      {"$or":[{"name":{"$like":"%11%"}},{"code":{"$eq":"123"}}]} -- 允许一层嵌套
     *  ]}
     *  @param  mongoExp: 匹配条件
     *  @param  dataObj: 数据
     */
    public static boolean compare(String mongoExp, String dataObj){
        if (StrUtil.isBlank(mongoExp)){
            return true;
        }
        Map<String, Object> dataCondMap = JsonUtil.stringToCollect(mongoExp);    //匹配条件
        Map<String, Object> objectMap = JsonUtil.stringToCollect(dataObj);  //数据
        log.debug("dataCondMap={}, objectMap={}", dataCondMap, objectMap);
        if (dataCondMap.isEmpty()){
            return true;
        }
        if (dataCondMap.containsKey(C_AND)) {
            return checkDataMatchObj(objectMap, dataCondMap.get(C_AND), C_AND) ;
        } else if (dataCondMap.containsKey(C_OR)) {
            return checkDataMatchObj(objectMap, dataCondMap.get(C_OR), C_OR) ;
        } else {
            return checkDataMatchMap(objectMap, dataCondMap, C_AND);
        }
    }

    /**
     *
     * @param objectMap 数据
     * @param map 匹配条件
     * @param type C_AND | C_OR
     * @return 匹配结果 true 匹配 false 不匹配
     */
    @SuppressWarnings("unchecked")
    private static boolean checkDataMatchMap(Map<String, Object> objectMap, Map<String, Object> map, String type){
        boolean isMatch = false;
        for (String key : map.keySet()){
            log.debug("property={}",key);
            if (C_AND.equals(key) || C_OR.equals(key)) {
                isMatch = checkDataMatchObj(objectMap, map.get(key), key);  //嵌套条件
            } else {
                Map<String, Object> valueMap = (Map<String, Object>)map.get(key);
                if (valueMap.keySet().size()!=1){
                    throw new BadRequestException("配置错误") ;
                }
                String symbol = null ;  //符号
                Object cValue = null ;  //配置的条件值
                for (String sol : valueMap.keySet()) {
                    symbol = sol;
                    cValue = valueMap.get(sol);  //拿到对应的配置条件值
                }
                Object oValue = objectMap.get(key) ; //数据里的值
                if (C_EQ.equals(symbol)) {
                    isMatch = Objects.equals(oValue, cValue);
                } else if (C_NE.equals(symbol)){
                    isMatch = oValue!=null && !oValue.equals(cValue) ;
                } else if (C_LIKE.equals(symbol)) {
                    isMatch = oValue != null && String.valueOf(oValue).contains(String.valueOf(cValue));
                } else if (C_GT.equals(symbol) || C_GTE.equals(symbol) || C_LT.equals(symbol) || C_LTE.equals(symbol) ){    //大于小于等这些只允许数值,日期要转换成毫秒数
                    if (oValue==null||cValue==null){
                        isMatch = false;
                    } else if (NumberUtil.isNumber(String.valueOf(oValue)) && NumberUtil.isNumber(String.valueOf(cValue))){
                        BigDecimal obigDecimal = new BigDecimal(String.valueOf(oValue)) ;
                        BigDecimal cbigDecimal = new BigDecimal(String.valueOf(cValue));
                        switch (symbol){
                            case C_GT :
                                isMatch = obigDecimal.compareTo(cbigDecimal)>0;
                                break;
                            case C_GTE:
                                isMatch = obigDecimal.compareTo(cbigDecimal)>=0;
                                break;
                            case C_LT:
                                isMatch = obigDecimal.compareTo(cbigDecimal)<0;
                                break;
                            default:
                                isMatch = obigDecimal.compareTo(cbigDecimal)<=0;
                        }
                    } else {
                        try {
                            // 日期字符串
                            Date cd = Convert.toDate(cValue);
                            Date od = Convert.toDate(oValue);
                            switch (symbol){
                                case C_GT :
                                    isMatch = od.after(cd);
                                    break;
                                case C_GTE:
                                    isMatch = od.compareTo(cd)>=0;
                                    break;
                                case C_LT:
                                    isMatch = od.before(cd);
                                    break;
                                default:
                                    isMatch = od.compareTo(cd)<=0;
                            }
                        } catch (Exception e){
                            log.warn("转日期出错",e);
                            isMatch = false;
                        }
                    }
                } else if (C_IN.equals(symbol)){
                    List<Object> cvList = (List<Object>) cValue;
                    isMatch = cvList.contains(oValue) ;
                } else if (C_NIN.equals(symbol)){
                    List<Object> cvList = (List<Object>) cValue;
                    isMatch = !cvList.contains(oValue);
                } else if (C_BETWEEN.equals(symbol)) {
                    List<Object> cvList = (List<Object>) cValue;
                    if (oValue == null) {
                        isMatch = false;
                    } else {
                        try {
                            Date od = Convert.toDate(oValue);
                            Date csd = Convert.toDate(cvList.get(0));
                            Date ced = Convert.toDate(cvList.get(1));
                            log.debug("日期区间条件：{} 是否在 [{}-{}]之间", od, csd, ced);
                            isMatch = od.after(csd) && od.before(ced);
                        } catch (Exception e){
                            log.warn("对象值转日期错误..", e);
                            isMatch = false;
                        }
                    }
                } else if (C_DY_NOW.equals(symbol)){
                    List<Object> cvList = (List<Object>) cValue;
                    if (oValue==null){
                        isMatch = false;
                    } else {
                        try{
                            Date od = Convert.toDate(oValue);
                            long days = DateUtil.between(new Date(),od, DateUnit.DAY, false);   //不是绝对值，允许负数
                            log.debug("动态条件：跟当前系统日期相差{}天", days);
                            BigDecimal dayDecimal = new BigDecimal(String.valueOf(days));
                            BigDecimal csDecimal = new BigDecimal(String.valueOf(cvList.get(0)));
                            BigDecimal ceDecimal = new BigDecimal(String.valueOf(cvList.get(1)));
                            log.debug("动态条件：{} 是否在 [{}-{}]之间", dayDecimal, csDecimal, ceDecimal);
                            isMatch = dayDecimal.compareTo(csDecimal) >= 0 && dayDecimal.compareTo(ceDecimal) <= 0;
                        } catch (Exception e){
                            log.warn("对象值转日期错误?",e);
                            isMatch = false;
                        }
                    }
                } else if (C_NULL.equals(symbol)){
                    isMatch = (oValue==null || "".equals(String.valueOf(oValue)));
                } else if (C_NOTNULL.equals(symbol)){
                    isMatch = oValue!=null && !"".equals(String.valueOf(oValue));
                }
                log.debug("条件字段{}-值:{}, 当前值：{} ,{} result:{}",key, cValue, oValue, symbol, isMatch);
            }
            if (C_OR.equals(type) && isMatch){    // or 只要有已个匹配返回 true
                return true;
            } else if (C_AND.equals(type) && !isMatch){ //and 只要有一个不匹配返回 false
                return false;
            }
        }

        return isMatch;
    }

    @SuppressWarnings("unchecked")
    private static boolean checkDataMatchObj(Map<String, Object> objectMap, Object obj, String type){
        boolean isMatch = false;
        if (obj instanceof Map) {
            Map<String, Object> map = (Map<String,Object>) obj ;
            return checkDataMatchMap(objectMap, map, type) ;
        } else if (obj instanceof List){
            List<Map<String,Object>> mapList = (List<Map<String,Object>>) obj;
            for (Map<String,Object> map : mapList) { //循环list
                isMatch = checkDataMatchMap(objectMap, map, type) ;
                if (C_OR.equals(type) && isMatch){    // or
                    return true;
                } else if (C_AND.equals(type) && !isMatch){
                    return false;
                }
            }
        }
        return isMatch;
    }



//    public static void main(String[] args) {

//        Map<String, Object> objectMap = new HashMap<>();
//        objectMap.put("warehouseId","2");
//        objectMap.put("itemName","牛肉");
//        objectMap.put("amount","2000.00");
//        String  str = "{\"$and\":[ {\"optime\":{\"$between\":[1614528000000,1616256000000]}}, {\"warehouseId\":{\"$in\":[1,3,4]}},{\"itemName\":{\"$like\":\"牛肉%\"}}, {\"$or\":[{\"price\":{\"$eq\":1.1}},{\"amount\":{\"$lt\":1000}}]}]}" ;
//        Map<String, Object> dataCondMap = JsonUtil.stringToCollect(str);    //匹配条件
//        if (dataCondMap.containsKey(C_AND)){
//            Object obj = dataCondMap.get(C_AND);
//            if (obj instanceof List){
//                List<Map<String,Object>> mapList = (List<Map<String,Object>>) obj;
//                for (Map<String,Object> map : mapList) { //循环list
//                    System.out.println(1);
//                    for (String property : map.keySet()){
//                        System.out.print(property);
//
//                        if (C_AND.equals(property) || C_OR.equals(property)) {
//
//                        } else {
//                            objectMap.get(property);
//
//                            Map<String, Object> valueMap =(Map<String, Object>)map.get(property);
//                            for (String symbol : valueMap.keySet()) {
//                                System.out.print(symbol);
//                                System.out.println(valueMap.get(symbol));
//                            }
//                        }
//                    }
//                }
//            }
//
//        }
//        String json = "{\"a1\":\"22\",\"a2\":\"333\"}" ;
//        JSONObject jsonObject = JSON.parseObject(json) ;
//        System.out.println(jsonObject.getInteger("a1"));
//        System.out.println(jsonObject.getInteger("tenantId"));
//        System.out.println(jsonObject.getString("a2"));
//        Map<String, String> map= new HashMap<>();
//        map.put("a", "xxx") ;
//        map.put("id","oig222");
//        String str = "单号{id},发生变更" ;
//        str = StrUtil.format(str,map);
//        System.out.println(str);
//    }

}
